<!-- 单图上传组件 -->
<template>
  <div class="upload-container">
    <el-upload
      v-model="modelValue"
      class="single-upload"
      list-type="picture-card"
      :show-file-list="false"
      :accept="props.accept"
      :before-upload="handleBeforeUpload"
      :http-request="handleUpload"
      :on-success="onSuccess"
      :on-error="onError"
    >
      <div class="upload-content">
        <!-- 已上传图片展示 -->
        <template v-if="modelValue">
          <!-- <div :style="props.style"> -->
          <el-image :src="modelValue" class="preview-image" fit="cover" />
          <!-- </div> -->

          <div class="hover-mask">
            <!-- <el-icon class="hover-icon"><View /></el-icon> -->
            <el-icon class="delete-icon" @click.stop="handleDelete">
              <Delete />
            </el-icon>
          </div>
        </template>

        <!-- 上传按钮 -->
        <template v-else>
          <div class="upload-trigger" :style="props.style">
            <el-icon class="upload-icon"><Plus /></el-icon>
            <!-- <span class="upload-text">点击上传</span> -->
          </div>
        </template>
      </div>
    </el-upload>
    <div style="font-size: 12px; color: #4e5969; margin-top: 2px">
      （支持扩展名:{{ props.accept }}）
    </div>
  </div>
</template>

<script setup lang="ts">
import { Plus, View, Delete } from "@element-plus/icons-vue";
import { UploadRawFile, UploadRequestOptions } from "element-plus";
import FileAPI from "@/api/file";

const props = defineProps({
  /**
   * 请求携带的额外参数
   */
  data: {
    type: Object,
    default: () => {
      return {};
    },
  },
  /**
   * 上传文件的参数名
   */
  name: {
    type: String,
    default: "file",
  },
  /**
   * 最大文件大小（单位：M）
   */
  maxFileSize: {
    type: Number,
    default: 50,
  },

  /**
   * 上传图片格式，默认支持所有图片(image/*)，指定格式示例：'.png,.jpg,.jpeg,.gif,.bmp'
   */
  accept: {
    type: String,
    default: "image/*",
  },

  /**
   * 自定义样式，用于设置组件的宽度和高度等其他样式
   */
  style: {
    type: Object,
    default: () => {
      return {
        width: "150px",
        height: "150px",
      };
    },
  },
});

const modelValue = defineModel("modelValue", {
  type: String,
  default: () => "",
});

/**
 * 限制用户上传文件的格式和大小
 */
function handleBeforeUpload(file: UploadRawFile) {
  // 校验文件类型：虽然 accept 属性限制了用户在文件选择器中可选的文件类型，但仍需在上传时再次校验文件实际类型，确保符合 accept 的规则
  const acceptTypes = props.accept.split(",").map((type) => type.trim());

  // 检查文件格式是否符合 accept
  const isValidType = acceptTypes.some((type) => {
    if (type === "image/*") {
      // 如果是 image/*，检查 MIME 类型是否以 "image/" 开头
      return file.type.startsWith("image/");
    } else if (type.startsWith(".")) {
      // 如果是扩展名 (.png, .jpg)，检查文件名是否以指定扩展名结尾
      return file.name.toLowerCase().endsWith(type);
    } else {
      // 如果是具体的 MIME 类型 (image/png, image/jpeg)，检查是否完全匹配
      return file.type === type;
    }
  });

  if (!isValidType) {
    ElMessage.warning(`仅支持格式: ${props.accept}`);
    return false;
  }

  // 限制文件大小
  if (file.size > props.maxFileSize * 1024 * 1024) {
    ElMessage.warning(`图片不能大于 ${props.maxFileSize}M`);
    return false;
  }
  return true;
}

/*
 * 上传图片
 */
function handleUpload(options: UploadRequestOptions) {
  return new Promise((resolve, reject) => {
    const file = options.file;
    const formData = new FormData();
    formData.append(props.name, file);

    // 处理附加参数
    Object.keys(props.data).forEach((key) => {
      formData.append(key, props.data[key]);
    });

    FileAPI.upload(file, props.data.fileType)
      .then((data) => {
        resolve(data);
      })
      .catch((error) => {
        reject(error);
      });
  });
}

/**
 * 删除图片
 */
function handleDelete() {
  modelValue.value = "";
}

/**
 * 上传成功回调
 *
 * @param res 上传成功后的文件信息
 */
const onSuccess = (res: string) => {
  ElMessage.success("上传成功");
  modelValue.value = res;
};

/**
 * 上传失败回调
 */
const onError = (error: any) => {
  console.log("onError");
  ElMessage.error("上传失败");
};
</script>

<script lang="ts">
export default {
  name: "SingleImageUpload",
};
</script>

<style scoped lang="scss">
.upload-container {
  display: flex;
  flex-direction: column;
}
.single-upload {
  :deep(.el-upload--picture-card) {
    width: v-bind("props.style.width");
    height: v-bind("props.style.height");
    border: 2px dashed var(--el-border-color-lighter);
    border-radius: 8px;
    transition: all 0.3s;

    &:hover {
      border-color: var(--el-color-primary);
    }
  }

  .upload-content {
    width: 100%;
    height: 100%;
    position: relative;
  }

  .preview-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 6px;
  }

  .hover-mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 6px;
    opacity: 0;
    transition: all 0.3s;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 12px;

    .hover-icon,
    .delete-icon {
      font-size: 24px;
      color: #fff;
      cursor: pointer;
      padding: 8px;
      border-radius: 50%;
      background: rgba(255, 255, 255, 0.2);
      transition: all 0.2s;

      &:hover {
        transform: scale(1.1);
        background: rgba(255, 255, 255, 0.3);
      }
    }
  }

  &:hover .hover-mask {
    opacity: 1;
  }

  .upload-trigger {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 100%;

    .upload-icon {
      font-size: 28px;
      color: var(--el-text-color-secondary);
      margin-bottom: 8px;
    }

    .upload-text {
      font-size: 14px;
      color: var(--el-text-color-secondary);
    }
  }
}
</style>
